package com.liveinstars.im.handler;

import com.liveinstars.im.common.enums.PassThroughMessageTypeEnum;
import com.liveinstars.im.corebean.*;
import com.liveinstars.im.entity.ChatMessage;
import com.liveinstars.im.entity.Msg;
import com.liveinstars.im.util.StringUtils;
import com.liveinstars.im.core.SessionManager;
import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.Bucket4j;
import io.github.bucket4j.Refill;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;

/**
 * 主消息分发
 * 
 * @author ting
 *
 */
@Sharable
@Service
public class MsgChatHandler extends SimpleChannelInboundHandler<Msg.Message> {

	private static final Logger LOGGER = LoggerFactory.getLogger(MsgChatHandler.class);

	@Value("${im.close.switch}")
	public boolean closeSwitch;

	@Autowired
	private SessionManager sessionManager;

	@Override
	protected void channelRead0(ChannelHandlerContext channelHandlerContext,
			Msg.Message message) throws Exception {
		switch (message.getMessageType()) {
		case CLIENT_LOGIN:
			if (message.getClientLoginMessage() != null) {
				ClientLoginMessage msg = new ClientLoginMessage();
				msg.setToken(message.getClientLoginMessage().getToken());
				msg.setUserId(message.getClientLoginMessage().getUserId());
				msg.setClientId(message.getClientLoginMessage().getClientId());
				msg.setClientType(message.getClientLoginMessage().getClientType());
				channelHandlerContext.fireChannelRead(msg);
			} else {
				LOGGER.info("消息异常，用户登录消息为null。");
			}
			break;
		case CLIENT_PONG:
			if (message.getPingMessage() != null) {
				LOGGER.info("client pong消息不用处理");
			} else {
				LOGGER.info("消息异常，client pong消息为null或者消息类型异常。");
			}
			break;
		case CLIENT_PING:
			if (message.getPingMessage() != null) {
				PingMessage msg = new PingMessage();
				msg.setClientId(message.getPingMessage().getClientId());
				msg.setMessageType(Msg.MessageType.CLIENT_PING);
				channelHandlerContext.fireChannelRead(msg);
			} else {
				LOGGER.info("消息异常，client ping消息为null或者消息类型异常。");
			}
			break;
		case SERVER_LOGIN:
			if (message.getServerLoginMessage() != null) {
				ServerLoginMessage msg = new ServerLoginMessage();
				msg.setIp(message.getServerLoginMessage().getIp());
				msg.setPort(message.getServerLoginMessage().getPort());
				LOGGER.info("MsgChatHandler receive server_login" + msg.getIp() + ":" + msg.getPort()
				);
				channelHandlerContext.fireChannelRead(msg);
			}
			break;
		case SERVER_PING:
			if (message.getPingMessage() != null) {
				PingMessage msg = new PingMessage();
				msg.setClientId(message.getPingMessage().getClientId());
				msg.setMessageType(Msg.MessageType.SERVER_PING);
				channelHandlerContext.fireChannelRead(msg);
			} else {
				LOGGER.info("消息异常，client ping消息为null或者消息类型异常。");
			}
			break;
		case SERVER_PONG:
			LOGGER.info("server pong消息不用处理");
			break;
		case CHAT_MESSAGE:
			if (closeSwitch) {
				Msg.Message msg = MsgHelper.newReceiptMessage(
						message.getChatMessage().getUuid(), ChatMessage.STATUS_CLOSE_MESSAGE);
				channelHandlerContext.channel().writeAndFlush(msg);
			} else if (message.getChatMessage() != null) {
				Bucket bucket = SessionManager.LOCAL_CACHE.computeIfAbsent(String.valueOf(message.getChatMessage().getFromId()), k -> createNewBucket(1, 1, Duration.ofSeconds(1)));
				if (bucket.tryConsume(1)) {
					LOGGER.info("MsgChatHandler receive CHAT_MESSAGE {}", message.getChatMessage().toString());
					ChatMessage msg = new ChatMessage();
					msg.setContent(message.getChatMessage().getContent());
					msg.setFromId(message.getChatMessage().getFromId());
					msg.setToId(message.getChatMessage().getToId());
					if(!StringUtils.isEmpty(message.getChatMessage().getToken())) {
						msg.setToken(message.getChatMessage().getToken());
					}
					msg.setTransfer(message.getChatMessage().getTransfer());
					msg.setType(ChatMessage.TYPE_SEND);
					msg.setMsgType(message.getChatMessage().getMsgType());
					msg.setChatGroupId(message.getChatMessage().getChatGroupId());
					if(message.getChatMessage().getChatMessageId() != 0) {
						msg.setId(message.getChatMessage().getChatMessageId());
					}
					msg.setUuid(message.getChatMessage().getUuid());
					msg.setStatus(ChatMessage.STATUS_SEND);
					// 都以当前服务器时间为准
					msg.setDate((int)LocalDateTime.now().atZone(ZoneId.systemDefault()).toEpochSecond());
					msg.setContentType(message.getChatMessage().getContentType());

					msg.setFileName(message.getChatMessage().getFileName());
					msg.setFileType(message.getChatMessage().getFileType());
					msg.setPath(message.getChatMessage().getFilePath());
					msg.setFileParam(message.getChatMessage().getFileParam());
					msg.setUserGroupId(message.getChatMessage().getUserGroupId());
					msg.setUserName(message.getChatMessage().getUsername());
					msg.setUserAvator(message.getChatMessage().getUseravator());
					channelHandlerContext.fireChannelRead(msg);
				} else {
					Msg.Message msg = MsgHelper.newReceiptMessage(
							message.getChatMessage().getUuid(), ChatMessage.STATUS_RATE_LIMIT_MESSAGE);
					channelHandlerContext.channel().writeAndFlush(msg);
				}
			} else {
				LOGGER.info("消息异常，chat message消息为null。");
			}
			break;
		case CLIENT_REQUEST:
			if (message.getClientRequestMessage() != null) {
				ClientRequestMessage r = new ClientRequestMessage();
				r.setParameter(message.getClientRequestMessage().getParameter());
				r.setRequestType(message.getClientRequestMessage()
						.getRequestType());
				r.setUserId(message.getClientRequestMessage().getUserId());
				r.setToken(message.getClientRequestMessage().getToken());
				channelHandlerContext.fireChannelRead(r);
			} else {
				LOGGER.info("消息异常，ClientRequest message消息为null。");
			}
			break;
		case TODO:
			//如果是todo类型，说明是其他服务器转发过来，用于下发的
			if(message.getTodoMessage() != null){
				LOGGER.info("MsgChatHandler shoudao TODO toid:{}",message.getTodoMessage().getToId());
				sessionManager.sendMessage(message.getTodoMessage().getToId(), message);
			}
			break;
		case PASS_THROUGH_MESSAGE:
			//服务之间的透传消息
			if (message.getPassThroughMessage() != null) {
				//用户消息透传
				if (message.getPassThroughMessage().getType() == PassThroughMessageTypeEnum.USER.getValue()) {
					sessionManager.sendMessage(message.getPassThroughMessage().getToId(), message.getPassThroughMessage().getMessage());
				} else {
					//TODO 待扩展
				}
			}
			break;
		default:
			break;
		}
		ReferenceCountUtil.release(message);
	}

	/**
	 * 发生异常时如何处理
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
			throws Exception {
		LOGGER.info("发生异常,用户将下线，session 将被移除，channel 将被关闭！"+ cause.getMessage(), cause);
		// 用户下线
		sessionManager.remove(ctx.channel().id().toString());
		ctx.channel().close();
		ctx.close();
	}

	private Bucket createNewBucket(int capacity, int refillTokens, Duration refillDuration){
		Refill refill = Refill.of(refillTokens, refillDuration);
		Bandwidth limit = Bandwidth.classic(capacity, refill);
		return Bucket4j.builder().addLimit(limit).build();
	}
}